home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Gfx / Edit / TSMorph / src / jpeg_ls / jmemmgr.c < prev    next >
C/C++ Source or Header  |  1994-10-30  |  38KB  |  1,103 lines

  1. /*
  2.  * jmemmgr.c
  3.  *
  4.  * Copyright (C) 1991, 1992, Thomas G. Lane.
  5.  * This file is part of the Independent JPEG Group's software.
  6.  * For conditions of distribution and use, see the accompanying README file.
  7.  *
  8.  * This file provides the standard system-independent memory management
  9.  * routines.  This code is usable across a wide variety of machines; most
  10.  * of the system dependencies have been isolated in a separate file.
  11.  * The major functions provided here are:
  12.  *   * bookkeeping to allow all allocated memory to be freed upon exit;
  13.  *   * policy decisions about how to divide available memory among the
  14.  *     various large arrays;
  15.  *   * control logic for swapping virtual arrays between main memory and
  16.  *     backing storage.
  17.  * The separate system-dependent file provides the actual backing-storage
  18.  * access code, and it contains the policy decision about how much total
  19.  * main memory to use.
  20.  * This file is system-dependent in the sense that some of its functions
  21.  * are unnecessary in some systems.  For example, if there is enough virtual
  22.  * memory so that backing storage will never be used, much of the big-array
  23.  * control logic could be removed.  (Of course, if you have that much memory
  24.  * then you shouldn't care about a little bit of unused code...)
  25.  *
  26.  * These routines are invoked via the methods alloc_small, free_small,
  27.  * alloc_medium, free_medium, alloc_small_sarray, free_small_sarray,
  28.  * alloc_small_barray, free_small_barray, request_big_sarray,
  29.  * request_big_barray, alloc_big_arrays, access_big_sarray, access_big_barray,
  30.  * free_big_sarray, free_big_barray, and free_all.
  31.  */
  32.  
  33. #define AM_MEMORY_MANAGER    /* we define big_Xarray_control structs */
  34.  
  35. #include "jinclude.h"
  36. #include "jmemsys.h"        /* import the system-dependent declarations */
  37.  
  38. #ifndef NO_GETENV
  39. #ifdef INCLUDES_ARE_ANSI
  40. #include <stdlib.h>        /* to declare getenv() */
  41. #else
  42. extern char * getenv PP((const char * name));
  43. #endif
  44. #endif
  45.  
  46.  
  47. /*
  48.  * On many systems it is not necessary to distinguish alloc_small from
  49.  * alloc_medium; the main case where they must be distinguished is when
  50.  * FAR pointers are distinct from regular pointers.  However, you might
  51.  * want to keep them separate if you have different system-dependent logic
  52.  * for small and large memory requests (i.e., jget_small and jget_large
  53.  * do different things).
  54.  */
  55.  
  56. #ifdef NEED_FAR_POINTERS
  57. #define NEED_ALLOC_MEDIUM    /* flags alloc_medium really exists */
  58. #endif
  59.  
  60.  
  61. /*
  62.  * Many machines require storage alignment: longs must start on 4-byte
  63.  * boundaries, doubles on 8-byte boundaries, etc.  On such machines, malloc()
  64.  * always returns pointers that are multiples of the worst-case alignment
  65.  * requirement, and we had better do so too.  This means the headers that
  66.  * we tack onto allocated structures had better have length a multiple of
  67.  * the alignment requirement.
  68.  * There isn't any really portable way to determine the worst-case alignment
  69.  * requirement.  In this code we assume that the alignment requirement is
  70.  * multiples of sizeof(align_type).  Here we define align_type as double;
  71.  * with this definition, the code will run on all machines known to me.
  72.  * If your machine has lesser alignment needs, you can save a few bytes
  73.  * by making align_type smaller.
  74.  */
  75.  
  76. typedef double align_type;
  77.  
  78.  
  79. /*
  80.  * Some important notes:
  81.  *   The allocation routines provided here must never return NULL.
  82.  *   They should exit to error_exit if unsuccessful.
  83.  *
  84.  *   It's not a good idea to try to merge the sarray and barray routines,
  85.  *   even though they are textually almost the same, because samples are
  86.  *   usually stored as bytes while coefficients are shorts.  Thus, in machines
  87.  *   where byte pointers have a different representation from word pointers,
  88.  *   the resulting machine code could not be the same.
  89.  */
  90.  
  91.  
  92. static external_methods_ptr methods; /* saved for access to error_exit */
  93.  
  94.  
  95. #ifdef MEM_STATS        /* optional extra stuff for statistics */
  96.  
  97. /* These macros are the assumed overhead per block for malloc().
  98.  * They don't have to be accurate, but the printed statistics will be
  99.  * off a little bit if they are not.
  100.  */
  101. #define MALLOC_OVERHEAD  (SIZEOF(void *)) /* overhead for jget_small() */
  102. #define MALLOC_FAR_OVERHEAD  (SIZEOF(void FAR *)) /* for jget_large() */
  103.  
  104. static long total_num_small = 0;    /* total # of small objects alloced */
  105. static long total_bytes_small = 0;    /* total bytes requested */
  106. static long cur_num_small = 0;        /* # currently alloced */
  107. static long max_num_small = 0;        /* max simultaneously alloced */
  108.  
  109. #ifdef NEED_ALLOC_MEDIUM
  110. static long total_num_medium = 0;    /* total # of medium objects alloced */
  111. static long total_bytes_medium = 0;    /* total bytes requested */
  112. static long cur_num_medium = 0;        /* # currently alloced */
  113. static long max_num_medium = 0;        /* max simultaneously alloced */
  114. #endif
  115.  
  116. static long total_num_sarray = 0;    /* total # of sarray objects alloced */
  117. static long total_bytes_sarray = 0;    /* total bytes requested */
  118. static long cur_num_sarray = 0;        /* # currently alloced */
  119. static long max_num_sarray = 0;        /* max simultaneously alloced */
  120.  
  121. static long total_num_barray = 0;    /* total # of barray objects alloced */
  122. static long total_bytes_barray = 0;    /* total bytes requested */
  123. static long cur_num_barray = 0;        /* # currently alloced */
  124. static long max_num_barray = 0;        /* max simultaneously alloced */
  125.  
  126.  
  127. LOCAL void
  128. print_mem_stats (void)
  129. {
  130.   /* since this is only a debugging stub, we can cheat a little on the
  131.    * trace message mechanism... helpful 'cuz trace_message can't handle longs.
  132.    */
  133.   fprintf1(stderr, "total_num_small = %ld\n", total_num_small);
  134.   fprintf1(stderr, "total_bytes_small = %ld\n", total_bytes_small);
  135.   if (cur_num_small)
  136.     fprintf1(stderr, "cur_num_small = %ld\n", cur_num_small);
  137.   fprintf1(stderr, "max_num_small = %ld\n", max_num_small);
  138.   
  139. #ifdef NEED_ALLOC_MEDIUM
  140.   fprintf1(stderr, "total_num_medium = %ld\n", total_num_medium);
  141.   fprintf1(stderr, "total_bytes_medium = %ld\n", total_bytes_medium);
  142.   if (cur_num_medium)
  143.     fprintf1(stderr, "cur_num_medium = %ld\n", cur_num_medium);
  144.   fprintf1(stderr, "max_num_medium = %ld\n", max_num_medium);
  145. #endif
  146.   
  147.   fprintf1(stderr, "total_num_sarray = %ld\n", total_num_sarray);
  148.   fprintf1(stderr, "total_bytes_sarray = %ld\n", total_bytes_sarray);
  149.   if (cur_num_sarray)
  150.     fprintf1(stderr, "cur_num_sarray = %ld\n", cur_num_sarray);
  151.   fprintf1(stderr, "max_num_sarray = %ld\n", max_num_sarray);
  152.   
  153.   fprintf1(stderr, "total_num_barray = %ld\n", total_num_barray);
  154.   fprintf1(stderr, "total_bytes_barray = %ld\n", total_bytes_barray);
  155.   if (cur_num_barray)
  156.     fprintf1(stderr, "cur_num_barray = %ld\n", cur_num_barray);
  157.   fprintf1(stderr, "max_num_barray = %ld\n", max_num_barray);
  158. }
  159.  
  160. #endif /* MEM_STATS */
  161.  
  162.  
  163. LOCAL void
  164. out_of_memory (int which)
  165. /* Report an out-of-memory error and stop execution */
  166. /* If we compiled MEM_STATS support, report alloc requests before dying */
  167. {
  168. #ifdef MEM_STATS
  169.   if (methods->trace_level <= 0) /* don't do it if free_all() will */
  170.     print_mem_stats();        /* print optional memory usage statistics */
  171. #endif
  172.   ERREXIT1(methods, "Insufficient memory (case %ld)", which);
  173. }
  174.  
  175.  
  176. /*
  177.  * Management of "small" objects.
  178.  * These are all-in-memory, and are in near-heap space on an 80x86.
  179.  */
  180.  
  181. typedef union small_struct * small_ptr;
  182.  
  183. typedef union small_struct {
  184.     small_ptr next;        /* next in list of allocated objects */
  185.     align_type dummy;    /* ensures alignment of following storage */
  186.       } small_hdr;
  187.  
  188. static small_ptr small_list;    /* head of list */
  189.  
  190.  
  191. METHODDEF void *
  192. alloc_small (size_t sizeofobject)
  193. /* Allocate a "small" object */
  194. {
  195.   small_ptr result;
  196.  
  197.   sizeofobject += SIZEOF(small_hdr); /* add space for header */
  198.  
  199. #ifdef MEM_STATS
  200.   total_num_small++;
  201.   total_bytes_small += sizeofobject + MALLOC_OVERHEAD;
  202.   cur_num_small++;
  203.   if (cur_num_small > max_num_small) max_num_small = cur_num_small;
  204. #endif
  205.  
  206.   result = (small_ptr) jget_small(sizeofobject);
  207.   if (result == NULL)
  208.     out_of_memory(1);
  209.  
  210.   result->next = small_list;
  211.   small_list = result;
  212.   result++;            /* advance past header */
  213.  
  214.   return (void *) result;
  215. }
  216.  
  217.  
  218. METHODDEF void
  219. free_small (void *ptr)
  220. /* Free a "small" object */
  221. {
  222.   small_ptr hdr;
  223.   small_ptr * llink;
  224.  
  225.   hdr = (small_ptr) ptr;
  226.   hdr--;            /* point back to header */
  227.  
  228.   /* Remove item from list -- linear search is fast enough */
  229.   llink = &small_list;
  230.   while (*llink != hdr) {
  231.     if (*llink == NULL)
  232.       ERREXIT(methods, "Bogus free_small request");
  233.     llink = &( (*llink)->next );
  234.   }
  235.   *llink = hdr->next;
  236.  
  237.   jfree_small((void *) hdr);
  238.  
  239. #ifdef MEM_STATS
  240.   cur_num_small--;
  241. #endif
  242. }
  243.  
  244.  
  245. /*
  246.  * Management of "medium-size" objects.
  247.  * These are just like small objects except they are in the FAR heap.
  248.  */
  249.  
  250. #ifdef NEED_ALLOC_MEDIUM
  251.  
  252. typedef union medium_struct FAR * medium_ptr;
  253.  
  254. typedef union medium_struct {
  255.     medium_ptr next;    /* next in list of allocated objects */
  256.     align_type dummy;    /* ensures alignment of following storage */
  257.       } medium_hdr;
  258.  
  259. static medium_ptr medium_list;    /* head of list */
  260.  
  261.  
  262. METHODDEF void FAR *
  263. alloc_medium (size_t sizeofobject)
  264. /* Allocate a "medium-size" object */
  265. {
  266.   medium_ptr result;
  267.  
  268.   sizeofobject += SIZEOF(medium_hdr); /* add space for header */
  269.  
  270. #ifdef MEM_STATS
  271.   total_num_medium++;
  272.   total_bytes_medium += sizeofobject + MALLOC_FAR_OVERHEAD;
  273.   cur_num_medium++;
  274.   if (cur_num_medium > max_num_medium) max_num_medium = cur_num_medium;
  275. #endif
  276.  
  277.   result = (medium_ptr) jget_large(sizeofobject);
  278.   if (result == NULL)
  279.     out_of_memory(2);
  280.  
  281.   result->next = medium_list;
  282.   medium_list = result;
  283.   result++;            /* advance past header */
  284.  
  285.   return (void FAR *) result;
  286. }
  287.  
  288.  
  289. METHODDEF void
  290. free_medium (void FAR *ptr)
  291. /* Free a "medium-size" object */
  292. {
  293.   medium_ptr hdr;
  294.   medium_ptr FAR * llink;
  295.  
  296.   hdr = (medium_ptr) ptr;
  297.   hdr--;            /* point back to header */
  298.  
  299.   /* Remove item from list -- linear search is fast enough */
  300.   llink = &medium_list;
  301.   while (*llink != hdr) {
  302.     if (*llink == NULL)
  303.       ERREXIT(methods, "Bogus free_medium request");
  304.     llink = &( (*llink)->next );
  305.   }
  306.   *llink = hdr->next;
  307.  
  308.   jfree_large((void FAR *) hdr);
  309.  
  310. #ifdef MEM_STATS
  311.   cur_num_medium--;
  312. #endif
  313. }
  314.  
  315. #endif /* NEED_ALLOC_MEDIUM */
  316.  
  317.  
  318. /*
  319.  * Management of "small" (all-in-memory) 2-D sample arrays.
  320.  * The pointers are in near heap, the samples themselves in FAR heap.
  321.  * The header structure is adjacent to the row pointers.
  322.  * To minimize allocation overhead and to allow I/O of large contiguous
  323.  * blocks, we allocate the sample rows in groups of as many rows as possible
  324.  * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request.
  325.  * Note that the big-array control routines, later in this file, know about
  326.  * this chunking of rows ... and also how to get the rowsperchunk value!
  327.  */
  328.  
  329. typedef struct small_sarray_struct * small_sarray_ptr;
  330.  
  331. typedef struct small_sarray_struct {
  332.     small_sarray_ptr next;    /* next in list of allocated sarrays */
  333.     long numrows;        /* # of rows in this array */
  334.     long rowsperchunk;    /* max # of rows per allocation chunk */
  335.     JSAMPROW dummy;        /* ensures alignment of following storage */
  336.       } small_sarray_hdr;
  337.  
  338. static small_sarray_ptr small_sarray_list; /* head of list */
  339.  
  340.  
  341. METHODDEF JSAMPARRAY
  342. alloc_small_sarray (long samplesperrow, long numrows)
  343. /* Allocate a "small" (all-in-memory) 2-D sample array */
  344. {
  345.   small_sarray_ptr hdr;
  346.   JSAMPARRAY result;
  347.   JSAMPROW workspace;
  348.   long rowsperchunk, currow, i;
  349.  
  350. #ifdef MEM_STATS
  351.   total_num_sarray++;
  352.   cur_num_sarray++;
  353.   if (cur_num_sarray > max_num_sarray) max_num_sarray = cur_num_sarray;
  354. #endif
  355.  
  356.   /* Calculate max # of rows allowed in one allocation chunk */
  357.   rowsperchunk = MAX_ALLOC_CHUNK / (samplesperrow * SIZEOF(JSAMPLE));
  358.   if (rowsperchunk <= 0)
  359.       ERREXIT(methods, "Image too wide for this implementation");
  360.  
  361.   /* Get space for header and row pointers; this is always "near" on 80x86 */
  362.   hdr = (small_sarray_ptr) alloc_small((size_t) (numrows * SIZEOF(JSAMPROW)
  363.                          + SIZEOF(small_sarray_hdr)));
  364.  
  365.   result = (JSAMPARRAY) (hdr+1); /* advance past header */
  366.  
  367.   /* Insert into list now so free_all does right thing if I fail */
  368.   /* after allocating only some of the rows... */
  369.   hdr->next = small_sarray_list;
  370.   hdr->numrows = 0;
  371.   hdr->rowsperchunk = rowsperchunk;
  372.   small_sarray_list = hdr;
  373.  
  374.   /* Get the rows themselves; on 80x86 these are "far" */
  375.   currow = 0;
  376.   while (currow < numrows) {
  377.     rowsperchunk = MIN(rowsperchunk, numrows - currow);
  378. #ifdef MEM_STATS
  379.     total_bytes_sarray += rowsperchunk * samplesperrow * SIZEOF(JSAMPLE)
  380.               + MALLOC_FAR_OVERHEAD;
  381. #endif
  382.     workspace = (JSAMPROW) jget_large((size_t) (rowsperchunk * samplesperrow
  383.                         * SIZEOF(JSAMPLE)));
  384.     if (workspace == NULL)
  385.       out_of_memory(3);
  386.     for (i = rowsperchunk; i > 0; i--) {
  387.       result[currow++] = workspace;
  388.       workspace += samplesperrow;
  389.     }
  390.     hdr->numrows = currow;
  391.   }
  392.  
  393.   return result;
  394. }
  395.  
  396.  
  397. METHODDEF void
  398. free_small_sarray (JSAMPARRAY ptr)
  399. /* Free a "small" (all-in-memory) 2-D sample array */
  400. {
  401.   small_sarray_ptr hdr;
  402.   small_sarray_ptr * llink;
  403.   long i;
  404.  
  405.   hdr = (small_sarray_ptr) ptr;
  406.   hdr--;            /* point back to header */
  407.  
  408.   /* Remove item from list -- linear search is fast enough */
  409.   llink = &small_sarray_list;
  410.   while (*llink != hdr) {
  411.     if (*llink == NULL)
  412.       ERREXIT(methods, "Bogus free_small_sarray request");
  413.     llink = &( (*llink)->next );
  414.   }
  415.   *llink = hdr->next;
  416.  
  417.   /* Free the rows themselves; on 80x86 these are "far" */
  418.   /* Note we only free the row-group headers! */
  419.   for (i = 0; i < hdr->numrows; i += hdr->rowsperchunk) {
  420.     jfree_large((void FAR *) ptr[i]);
  421.   }
  422.  
  423.   /* Free header and row pointers */
  424.   free_small((void *) hdr);
  425.  
  426. #ifdef MEM_STATS
  427.   cur_num_sarray--;
  428. #endif
  429. }
  430.  
  431.  
  432. /*
  433.  * Management of "small" (all-in-memory) 2-D coefficient-block arrays.
  434.  * This is essentially the same as the code for sample arrays, above.
  435.  */
  436.  
  437. typedef struct small_barray_struct * small_barray_ptr;
  438.  
  439. typedef struct small_barray_struct {
  440.     small_barray_ptr next;    /* next in list of allocated barrays */
  441.     long numrows;        /* # of rows in this array */
  442.     long rowsperchunk;    /* max # of rows per allocation chunk */
  443.     JBLOCKROW dummy;    /* ensures alignment of following storage */
  444.       } small_barray_hdr;
  445.  
  446. static small_barray_ptr small_barray_list; /* head of list */
  447.  
  448.  
  449. METHODDEF JBLOCKARRAY
  450. alloc_small_barray (long blocksperrow, long numrows)
  451. /* Allocate a "small" (all-in-memory) 2-D coefficient-block array */
  452. {
  453.   small_barray_ptr hdr;
  454.   JBLOCKARRAY result;
  455.   JBLOCKROW workspace;
  456.   long rowsperchunk, currow, i;
  457.  
  458. #ifdef MEM_STATS
  459.   total_num_barray++;
  460.   cur_num_barray++;
  461.   if (cur_num_barray > max_num_barray) max_num_barray = cur_num_barray;
  462. #endif
  463.  
  464.   /* Calculate max # of rows allowed in one allocation chunk */
  465.   rowsperchunk = MAX_ALLOC_CHUNK / (blocksperrow * SIZEOF(JBLOCK));
  466.   if (rowsperchunk <= 0)
  467.       ERREXIT(methods, "Image too wide for this implementation");
  468.  
  469.   /* Get space for header and row pointers; this is always "near" on 80x86 */
  470.   hdr = (small_barray_ptr) alloc_small((size_t) (numrows * SIZEOF(JBLOCKROW)
  471.                          + SIZEOF(small_barray_hdr)));
  472.  
  473.   result = (JBLOCKARRAY) (hdr+1); /* advance past header */
  474.  
  475.   /* Insert into list now so free_all does right thing if I fail */
  476.   /* after allocating only some of the rows... */
  477.   hdr->next = small_barray_list;
  478.   hdr->numrows = 0;
  479.   hdr->rowsperchunk = rowsperchunk;
  480.   small_barray_list = hdr;
  481.  
  482.   /* Get the rows themselves; on 80x86 these are "far" */
  483.   currow = 0;
  484.   while (currow < numrows) {
  485.     rowsperchunk = MIN(rowsperchunk, numrows - currow);
  486. #ifdef MEM_STATS
  487.     total_bytes_barray += rowsperchunk * blocksperrow * SIZEOF(JBLOCK)
  488.               + MALLOC_FAR_OVERHEAD;
  489. #endif
  490.     workspace = (JBLOCKROW) jget_large((size_t) (rowsperchunk * blocksperrow
  491.                          * SIZEOF(JBLOCK)));
  492.     if (workspace == NULL)
  493.       out_of_memory(4);
  494.     for (i = rowsperchunk; i > 0; i--) {
  495.       result[currow++] = workspace;
  496.       workspace += blocksperrow;
  497.     }
  498.     hdr->numrows = currow;
  499.   }
  500.  
  501.   return result;
  502. }
  503.  
  504.  
  505. METHODDEF void
  506. free_small_barray (JBLOCKARRAY ptr)
  507. /* Free a "small" (all-in-memory) 2-D coefficient-block array */
  508. {
  509.   small_barray_ptr hdr;
  510.   small_barray_ptr * llink;
  511.   long i;
  512.  
  513.   hdr = (small_barray_ptr) ptr;
  514.   hdr--;            /* point back to header */
  515.  
  516.   /* Remove item from list -- linear search is fast enough */
  517.   llink = &small_barray_list;
  518.   while (*llink != hdr) {
  519.     if (*llink == NULL)
  520.       ERREXIT(methods, "Bogus free_small_barray request");
  521.     llink = &( (*llink)->next );
  522.   }
  523.   *llink = hdr->next;
  524.  
  525.   /* Free the rows themselves; on 80x86 these are "far" */
  526.   /* Note we only free the row-group headers! */
  527.   for (i = 0; i < hdr->numrows; i += hdr->rowsperchunk) {
  528.     jfree_large((void FAR *) ptr[i]);
  529.   }
  530.  
  531.   /* Free header and row pointers */
  532.   free_small((void *) hdr);
  533.  
  534. #ifdef MEM_STATS
  535.   cur_num_barray--;
  536. #endif
  537. }
  538.  
  539.  
  540.  
  541. /*
  542.  * About "big" array management:
  543.  *
  544.  * To allow machines with limited memory to handle large images,
  545.  * all processing in the JPEG system is done a few pixel or block rows
  546.  * at a time.  The above "small" array routines are only used to allocate
  547.  * strip buffers (as wide as the image, but just a few rows high).
  548.  * In some cases multiple passes must be made over the data.  In these
  549.  * cases the "big" array routines are used.  The array is still accessed
  550.  * a strip at a time, but the memory manager must save the whole array
  551.  * for repeated accesses.  The intended implementation is that there is
  552.  * a strip buffer in memory (as high as is possible given the desired memory
  553.  * limit), plus a backing file that holds the rest of the array.
  554.  *
  555.  * The request_big_array routines are told the total size of the image (in case
  556.  * it is useful to know the total file size that will be needed).  They are
  557.  * also given the unit height, which is the number of rows that will be
  558.  * accessed at once; the in-memory buffer should be made a multiple of
  559.  * this height for best efficiency.
  560.  *
  561.  * The request routines create control blocks (and may open backing files),
  562.  * but they don't create the in-memory buffers.  This is postponed until
  563.  * alloc_big_arrays is called.  At that time the total amount of space needed
  564.  * is known (approximately, anyway), so free memory can be divided up fairly.
  565.  *
  566.  * The access_big_array routines are responsible for making a specific strip
  567.  * area accessible (after reading or writing the backing file, if necessary).
  568.  * Note that the access routines are told whether the caller intends to modify
  569.  * the accessed strip; during a read-only pass this saves having to rewrite
  570.  * data to disk.
  571.  *
  572.  * The typical access pattern is one top-to-bottom pass to write the data,
  573.  * followed by one or more read-only top-to-bottom passes.  However, other
  574.  * access patterns may occur while reading.  For example, translation of image
  575.  * formats that use bottom-to-top scan order will require bottom-to-top read
  576.  * passes.  The memory manager need not support multiple write passes nor
  577.  * funny write orders (meaning that rearranging rows must be handled while
  578.  * reading data out of the big array, not while putting it in).
  579.  *
  580.  * In current usage, the access requests are always for nonoverlapping strips;
  581.  * that is, successive access start_row numbers always differ by exactly the
  582.  * unitheight.  This allows fairly simple buffer dump/reload logic if the
  583.  * in-memory buffer is made a multiple of the unitheight.  It would be
  584.  * possible to keep downsampled rather than fullsize data in the "big" arrays,
  585.  * thus reducing temp file size, if we supported overlapping strip access
  586.  * (access requests differing by less than the unitheight).  At the moment
  587.  * I don't believe this is worth the extra complexity.
  588.  */
  589.  
  590.  
  591.  
  592. /* The control blocks for virtual arrays.
  593.  * System-dependent info for the associated backing store is hidden inside
  594.  * the backing_store_info struct.
  595.  */
  596.  
  597. struct big_sarray_control {
  598.     long rows_in_array;    /* total virtual array height */
  599.     long samplesperrow;    /* width of array (and of memory buffer) */
  600.     long unitheight;    /* # of rows accessed by access_big_sarray() */
  601.     JSAMPARRAY mem_buffer;    /* the in-memory buffer */
  602.     long rows_in_mem;    /* height of memory buffer */
  603.     long rowsperchunk;    /* allocation chunk size in mem_buffer */
  604.     long cur_start_row;    /* first logical row # in the buffer */
  605.     boolean dirty;        /* do current buffer contents need written? */
  606.     boolean b_s_open;    /* is backing-store data valid? */
  607.     big_sarray_ptr next;    /* link to next big sarray control block */
  608.     backing_store_info b_s_info; /* System-dependent control info */
  609. };
  610.  
  611. static big_sarray_ptr big_sarray_list; /* head of list */
  612.  
  613. struct big_barray_control {
  614.     long rows_in_array;    /* total virtual array height */
  615.     long blocksperrow;    /* width of array (and of memory buffer) */
  616.     long unitheight;    /* # of rows accessed by access_big_barray() */
  617.     JBLOCKARRAY mem_buffer;    /* the in-memory buffer */
  618.     long rows_in_mem;    /* height of memory buffer */
  619.     long rowsperchunk;    /* allocation chunk size in mem_buffer */
  620.     long cur_start_row;    /* first logical row # in the buffer */
  621.     boolean dirty;        /* do current buffer contents need written? */
  622.     boolean b_s_open;    /* is backing-store data valid? */
  623.     big_barray_ptr next;    /* link to next big barray control block */
  624.     backing_store_info b_s_info; /* System-dependent control info */
  625. };
  626.  
  627. static big_barray_ptr big_barray_list; /* head of list */
  628.  
  629.  
  630. METHODDEF big_sarray_ptr
  631. request_big_sarray (long samplesperrow, long numrows, long unitheight)
  632. /* Request a "big" (virtual-memory) 2-D sample array */
  633. {
  634.   big_sarray_ptr result;
  635.  
  636.   /* get control block */
  637.   result = (big_sarray_ptr) alloc_small(SIZEOF(struct big_sarray_control));
  638.  
  639.   result->rows_in_array = numrows;
  640.   result->samplesperrow = samplesperrow;
  641.   result->unitheight = unitheight;
  642.   result->mem_buffer = NULL;    /* marks array not yet realized */
  643.   result->b_s_open = FALSE;    /* no associated backing-store object */
  644.   result->next = big_sarray_list; /* add to list of big arrays */
  645.   big_sarray_list = result;
  646.  
  647.   return result;
  648. }
  649.  
  650.  
  651. METHODDEF big_barray_ptr
  652. request_big_barray (long blocksperrow, long numrows, long unitheight)
  653. /* Request a "big" (virtual-memory) 2-D coefficient-block array */
  654. {
  655.   big_barray_ptr result;
  656.  
  657.   /* get control block */
  658.   result = (big_barray_ptr) alloc_small(SIZEOF(struct big_barray_control));
  659.  
  660.   result->rows_in_array = numrows;
  661.   result->blocksperrow = blocksperrow;
  662.   result->unitheight = unitheight;
  663.   result->mem_buffer = NULL;    /* marks array not yet realized */
  664.   result->b_s_open = FALSE;    /* no associated backing-store object */
  665.   result->next = big_barray_list; /* add to list of big arrays */
  666.   big_barray_list = result;
  667.  
  668.   return result;
  669. }
  670.  
  671.  
  672. METHODDEF void
  673. alloc_big_arrays (long extra_small_samples, long extra_small_blocks,
  674.           long extra_medium_space)
  675. /* Allocate the in-memory buffers for any unrealized "big" arrays */
  676. /* 'extra' values are upper bounds for total future small-array requests */
  677. /* and far-heap requests */
  678. {
  679.   long total_extra_space = extra_small_samples * SIZEOF(JSAMPLE)
  680.                + extra_small_blocks * SIZEOF(JBLOCK)
  681.                + extra_medium_space;
  682.   long space_per_unitheight, maximum_space, avail_mem;
  683.   long unitheights, max_unitheights;
  684.   big_sarray_ptr sptr;
  685.   big_barray_ptr bptr;
  686.  
  687.   /* Compute the minimum space needed (unitheight rows in each buffer)
  688.    * and the maximum space needed (full image height in each buffer).
  689.    * These may be of use to the system-dependent jmem_available routine.
  690.    */
  691.   space_per_unitheight = 0;
  692.   maximum_space = total_extra_space;
  693.   for (sptr = big_sarray_list; sptr != NULL; sptr = sptr->next) {
  694.     if (sptr->mem_buffer == NULL) { /* if not realized yet */
  695.       space_per_unitheight += sptr->unitheight *
  696.                   sptr->samplesperrow * SIZEOF(JSAMPLE);
  697.       maximum_space += sptr->rows_in_array *
  698.                sptr->samplesperrow * SIZEOF(JSAMPLE);
  699.     }
  700.   }
  701.   for (bptr = big_barray_list; bptr != NULL; bptr = bptr->next) {
  702.     if (bptr->mem_buffer == NULL) { /* if not realized yet */
  703.       space_per_unitheight += bptr->unitheight *
  704.                   bptr->blocksperrow * SIZEOF(JBLOCK);
  705.       maximum_space += bptr->rows_in_array *
  706.                bptr->blocksperrow * SIZEOF(JBLOCK);
  707.     }
  708.   }
  709.  
  710.   if (space_per_unitheight <= 0)
  711.     return;            /* no unrealized arrays, no work */
  712.  
  713.   /* Determine amount of memory to actually use; this is system-dependent. */
  714.   avail_mem = jmem_available(space_per_unitheight + total_extra_space,
  715.                  maximum_space);
  716.  
  717.   /* If the maximum space needed is available, make all the buffers full
  718.    * height; otherwise parcel it out with the same number of unitheights
  719.    * in each buffer.
  720.    */
  721.   if (avail_mem >= maximum_space)
  722.     max_unitheights = 1000000000L;
  723.   else {
  724.     max_unitheights = (avail_mem - total_extra_space) / space_per_unitheight;
  725.     /* If there doesn't seem to be enough space, try to get the minimum
  726.      * anyway.  This allows a "stub" implementation of jmem_available().
  727.      */
  728.     if (max_unitheights <= 0)
  729.       max_unitheights = 1;
  730.   }
  731.  
  732.   /* Allocate the in-memory buffers and initialize backing store as needed. */
  733.  
  734.   for (sptr = big_sarray_list; sptr != NULL; sptr = sptr->next) {
  735.     if (sptr->mem_buffer == NULL) { /* if not realized yet */
  736.       unitheights = (sptr->rows_in_array + sptr->unitheight - 1L)
  737.             / sptr->unitheight;
  738.       if (unitheights <= max_unitheights) {
  739.     /* This buffer fits in memory */
  740.     sptr->rows_in_mem = sptr->rows_in_array;
  741.       } else {
  742.     /* It doesn't fit in memory, create backing store. */
  743.     sptr->rows_in_mem = max_unitheights * sptr->unitheight;
  744.     jopen_backing_store(& sptr->b_s_info,
  745.                 (long) (sptr->rows_in_array *
  746.                     sptr->samplesperrow * SIZEOF(JSAMPLE)));
  747.     sptr->b_s_open = TRUE;
  748.       }
  749.       sptr->mem_buffer = alloc_small_sarray(sptr->samplesperrow,
  750.                         sptr->rows_in_mem);
  751.       /* Reach into the small_sarray header and get the rowsperchunk field.
  752.        * Yes, I know, this is horrible coding practice.
  753.        */
  754.       sptr->rowsperchunk =
  755.     ((small_sarray_ptr) sptr->mem_buffer)[-1].rowsperchunk;
  756.       sptr->cur_start_row = 0;
  757.       sptr->dirty = FALSE;
  758.     }
  759.   }
  760.  
  761.   for (bptr = big_barray_list; bptr != NULL; bptr = bptr->next) {
  762.     if (bptr->mem_buffer == NULL) { /* if not realized yet */
  763.       unitheights = (bptr->rows_in_array + bptr->unitheight - 1L)
  764.             / bptr->unitheight;
  765.       if (unitheights <= max_unitheights) {
  766.     /* This buffer fits in memory */
  767.     bptr->rows_in_mem = bptr->rows_in_array;
  768.       } else {
  769.     /* It doesn't fit in memory, create backing store. */
  770.     bptr->rows_in_mem = max_unitheights * bptr->unitheight;
  771.     jopen_backing_store(& bptr->b_s_info,
  772.                 (long) (bptr->rows_in_array *
  773.                     bptr->blocksperrow * SIZEOF(JBLOCK)));
  774.     bptr->b_s_open = TRUE;
  775.       }
  776.       bptr->mem_buffer = alloc_small_barray(bptr->blocksperrow,
  777.                         bptr->rows_in_mem);
  778.       /* Reach into the small_barray header and get the rowsperchunk field. */
  779.       bptr->rowsperchunk =
  780.     ((small_barray_ptr) bptr->mem_buffer)[-1].rowsperchunk;
  781.       bptr->cur_start_row = 0;
  782.       bptr->dirty = FALSE;
  783.     }
  784.   }
  785. }
  786.  
  787.  
  788. LOCAL void
  789. do_sarray_io (big_sarray_ptr ptr, boolean writing)
  790. /* Do backing store read or write of a "big" sample array */
  791. {
  792.   long bytesperrow, file_offset, byte_count, rows, i;
  793.  
  794.   bytesperrow = ptr->samplesperrow * SIZEOF(JSAMPLE);
  795.   file_offset = ptr->cur_start_row * bytesperrow;
  796.   /* Loop to read or write each allocation chunk in mem_buffer */
  797.   for (i = 0; i < ptr->rows_in_mem; i += ptr->rowsperchunk) {
  798.     /* One chunk, but check for short chunk at end of buffer */
  799.     rows = MIN(ptr->rowsperchunk, ptr->rows_in_mem - i);
  800.     /* Transfer no more than fits in file */
  801.     rows = MIN(rows, ptr->rows_in_array - (ptr->cur_start_row + i));
  802.     if (rows <= 0)        /* this chunk might be past end of file! */
  803.       break;
  804.     byte_count = rows * bytesperrow;
  805.     if (writing)
  806.       (*ptr->b_s_info.write_backing_store) (& ptr->b_s_info,
  807.                         (void FAR *) ptr->mem_buffer[i],
  808.                         file_offset, byte_count);
  809.     else
  810.       (*ptr->b_s_info.read_backing_store) (& ptr->b_s_info,
  811.                        (void FAR *) ptr->mem_buffer[i],
  812.                        file_offset, byte_count);
  813.     file_offset += byte_count;
  814.   }
  815. }
  816.  
  817.  
  818. LOCAL void
  819. do_barray_io (big_barray_ptr ptr, boolean writing)
  820. /* Do backing store read or write of a "big" coefficient-block array */
  821. {
  822.   long bytesperrow, file_offset, byte_count, rows, i;
  823.  
  824.   bytesperrow = ptr->blocksperrow * SIZEOF(JBLOCK);
  825.   file_offset = ptr->cur_start_row * bytesperrow;
  826.   /* Loop to read or write each allocation chunk in mem_buffer */
  827.   for (i = 0; i < ptr->rows_in_mem; i += ptr->rowsperchunk) {
  828.     /* One chunk, but check for short chunk at end of buffer */
  829.     rows = MIN(ptr->rowsperchunk, ptr->rows_in_mem - i);
  830.     /* Transfer no more than fits in file */
  831.     rows = MIN(rows, ptr->rows_in_array - (ptr->cur_start_row + i));
  832.     if (rows <= 0)        /* this chunk might be past end of file! */
  833.       break;
  834.     byte_count = rows * bytesperrow;
  835.     if (writing)
  836.       (*ptr->b_s_info.write_backing_store) (& ptr->b_s_info,
  837.                         (void FAR *) ptr->mem_buffer[i],
  838.                         file_offset, byte_count);
  839.     else
  840.       (*ptr->b_s_info.read_backing_store) (& ptr->b_s_info,
  841.                        (void FAR *) ptr->mem_buffer[i],
  842.                        file_offset, byte_count);
  843.     file_offset += byte_count;
  844.   }
  845. }
  846.  
  847.  
  848. METHODDEF JSAMPARRAY
  849. access_big_sarray (big_sarray_ptr ptr, long start_row, boolean writable)
  850. /* Access the part of a "big" sample array starting at start_row */
  851. /* and extending for ptr->unitheight rows.  writable is true if  */
  852. /* caller intends to modify the accessed area. */
  853. {
  854.   /* debugging check */
  855.   if (start_row < 0 || start_row+ptr->unitheight > ptr->rows_in_array ||
  856.       ptr->mem_buffer == NULL)
  857.     ERREXIT(methods, "Bogus access_big_sarray request");
  858.  
  859.   /* Make the desired part of the virtual array accessible */
  860.   if (start_row < ptr->cur_start_row ||
  861.       start_row+ptr->unitheight > ptr->cur_start_row+ptr->rows_in_mem) {
  862.     if (! ptr->b_s_open)
  863.       ERREXIT(methods, "Virtual array controller messed up");
  864.     /* Flush old buffer contents if necessary */
  865.     if (ptr->dirty) {
  866.       do_sarray_io(ptr, TRUE);
  867.       ptr->dirty = FALSE;
  868.     }
  869.     /* Decide what part of virtual array to access.
  870.      * Algorithm: if target address > current window, assume forward scan,
  871.      * load starting at target address.  If target address < current window,
  872.      * assume backward scan, load so that target address is top of window.
  873.      * Note that when switching from forward write to forward read, will have
  874.      * start_row = 0, so the limiting case applies and we load from 0 anyway.
  875.      */
  876.     if (start_row > ptr->cur_start_row) {
  877.       ptr->cur_start_row = start_row;
  878.     } else {
  879.       ptr->cur_start_row = start_row + ptr->unitheight - ptr->rows_in_mem;
  880.       if (ptr->cur_start_row < 0)
  881.     ptr->cur_start_row = 0;    /* don't fall off front end of file */
  882.     }
  883.     /* If reading, read in the selected part of the array. 
  884.      * If we are writing, we need not pre-read the selected portion,
  885.      * since the access sequence constraints ensure it would be garbage.
  886.      */
  887.     if (! writable) {
  888.       do_sarray_io(ptr, FALSE);
  889.     }
  890.   }
  891.   /* Flag the buffer dirty if caller will write in it */
  892.   if (writable)
  893.     ptr->dirty = TRUE;
  894.   /* Return address of proper part of the buffer */
  895.   return ptr->mem_buffer + (start_row - ptr->cur_start_row);
  896. }
  897.  
  898.  
  899. METHODDEF JBLOCKARRAY
  900. access_big_barray (big_barray_ptr ptr, long start_row, boolean writable)
  901. /* Access the part of a "big" coefficient-block array starting at start_row */
  902. /* and extending for ptr->unitheight rows.  writable is true if  */
  903. /* caller intends to modify the accessed area. */
  904. {
  905.   /* debugging check */
  906.   if (start_row < 0 || start_row+ptr->unitheight > ptr->rows_in_array ||
  907.       ptr->mem_buffer == NULL)
  908.     ERREXIT(methods, "Bogus access_big_barray request");
  909.  
  910.   /* Make the desired part of the virtual array accessible */
  911.   if (start_row < ptr->cur_start_row ||
  912.       start_row+ptr->unitheight > ptr->cur_start_row+ptr->rows_in_mem) {
  913.     if (! ptr->b_s_open)
  914.       ERREXIT(methods, "Virtual array controller messed up");
  915.     /* Flush old buffer contents if necessary */
  916.     if (ptr->dirty) {
  917.       do_barray_io(ptr, TRUE);
  918.       ptr->dirty = FALSE;
  919.     }
  920.     /* Decide what part of virtual array to access.
  921.      * Algorithm: if target address > current window, assume forward scan,
  922.      * load starting at target address.  If target address < current window,
  923.      * assume backward scan, load so that target address is top of window.
  924.      * Note that when switching from forward write to forward read, will have
  925.      * start_row = 0, so the limiting case applies and we load from 0 anyway.
  926.      */
  927.     if (start_row > ptr->cur_start_row) {
  928.       ptr->cur_start_row = start_row;
  929.     } else {
  930.       ptr->cur_start_row = start_row + ptr->unitheight - ptr->rows_in_mem;
  931.       if (ptr->cur_start_row < 0)
  932.     ptr->cur_start_row = 0;    /* don't fall off front end of file */
  933.     }
  934.     /* If reading, read in the selected part of the array. 
  935.      * If we are writing, we need not pre-read the selected portion,
  936.      * since the access sequence constraints ensure it would be garbage.
  937.      */
  938.     if (! writable) {
  939.       do_barray_io(ptr, FALSE);
  940.     }
  941.   }
  942.   /* Flag the buffer dirty if caller will write in it */
  943.   if (writable)
  944.     ptr->dirty = TRUE;
  945.   /* Return address of proper part of the buffer */
  946.   return ptr->mem_buffer + (start_row - ptr->cur_start_row);
  947. }
  948.  
  949.  
  950. METHODDEF void
  951. free_big_sarray (big_sarray_ptr ptr)
  952. /* Free a "big" (virtual-memory) 2-D sample array */
  953. {
  954.   big_sarray_ptr * llink;
  955.  
  956.   /* Remove item from list -- linear search is fast enough */
  957.   llink = &big_sarray_list;
  958.   while (*llink != ptr) {
  959.     if (*llink == NULL)
  960.       ERREXIT(methods, "Bogus free_big_sarray request");
  961.     llink = &( (*llink)->next );
  962.   }
  963.   *llink = ptr->next;
  964.  
  965.   if (ptr->b_s_open)        /* there may be no backing store */
  966.     (*ptr->b_s_info.close_backing_store) (& ptr->b_s_info);
  967.  
  968.   if (ptr->mem_buffer != NULL)    /* just in case never realized */
  969.     free_small_sarray(ptr->mem_buffer);
  970.  
  971.   free_small((void *) ptr);    /* free the control block too */
  972. }
  973.  
  974.  
  975. METHODDEF void
  976. free_big_barray (big_barray_ptr ptr)
  977. /* Free a "big" (virtual-memory) 2-D coefficient-block array */
  978. {
  979.   big_barray_ptr * llink;
  980.  
  981.   /* Remove item from list -- linear search is fast enough */
  982.   llink = &big_barray_list;
  983.   while (*llink != ptr) {
  984.     if (*llink == NULL)
  985.       ERREXIT(methods, "Bogus free_big_barray request");
  986.     llink = &( (*llink)->next );
  987.   }
  988.   *llink = ptr->next;
  989.  
  990.   if (ptr->b_s_open)        /* there may be no backing store */
  991.     (*ptr->b_s_info.close_backing_store) (& ptr->b_s_info);
  992.  
  993.   if (ptr->mem_buffer != NULL)    /* just in case never realized */
  994.     free_small_barray(ptr->mem_buffer);
  995.  
  996.   free_small((void *) ptr);    /* free the control block too */
  997. }
  998.  
  999.  
  1000. /*
  1001.  * Cleanup: free anything that's been allocated since jselmemmgr().
  1002.  */
  1003.  
  1004. METHODDEF void
  1005. free_all (void)
  1006. {
  1007.   /* First free any open "big" arrays -- these may release small arrays */
  1008.   while (big_sarray_list != NULL)
  1009.     free_big_sarray(big_sarray_list);
  1010.   while (big_barray_list != NULL)
  1011.     free_big_barray(big_barray_list);
  1012.   /* Free any open small arrays -- these may release small objects */
  1013.   /* +1's are because we must pass a pointer to the data, not the header */
  1014.   while (small_sarray_list != NULL)
  1015.     free_small_sarray((JSAMPARRAY) (small_sarray_list + 1));
  1016.   while (small_barray_list != NULL)
  1017.     free_small_barray((JBLOCKARRAY) (small_barray_list + 1));
  1018.   /* Free any remaining small objects */
  1019.   while (small_list != NULL)
  1020.     free_small((void *) (small_list + 1));
  1021. #ifdef NEED_ALLOC_MEDIUM
  1022.   while (medium_list != NULL)
  1023.     free_medium((void FAR *) (medium_list + 1));
  1024. #endif
  1025.  
  1026.   jmem_term();            /* system-dependent cleanup */
  1027.  
  1028. #ifdef MEM_STATS
  1029.   if (methods->trace_level > 0)
  1030.     print_mem_stats();        /* print optional memory usage statistics */
  1031. #endif
  1032. }
  1033.  
  1034.  
  1035. /*
  1036.  * The method selection routine for virtual memory systems.
  1037.  * The system-dependent setup routine should call this routine
  1038.  * to install the necessary method pointers in the supplied struct.
  1039.  */
  1040.  
  1041. GLOBAL void
  1042. jselmemmgr (external_methods_ptr emethods)
  1043. {
  1044.   methods = emethods;        /* save struct addr for error exit access */
  1045.  
  1046.   emethods->alloc_small = alloc_small;
  1047.   emethods->free_small = free_small;
  1048. #ifdef NEED_ALLOC_MEDIUM
  1049.   emethods->alloc_medium = alloc_medium;
  1050.   emethods->free_medium = free_medium;
  1051. #else
  1052.   emethods->alloc_medium = alloc_small;
  1053.   emethods->free_medium = free_small;
  1054. #endif
  1055.   emethods->alloc_small_sarray = alloc_small_sarray;
  1056.   emethods->free_small_sarray = free_small_sarray;
  1057.   emethods->alloc_small_barray = alloc_small_barray;
  1058.   emethods->free_small_barray = free_small_barray;
  1059.   emethods->request_big_sarray = request_big_sarray;
  1060.   emethods->request_big_barray = request_big_barray;
  1061.   emethods->alloc_big_arrays = alloc_big_arrays;
  1062.   emethods->access_big_sarray = access_big_sarray;
  1063.   emethods->access_big_barray = access_big_barray;
  1064.   emethods->free_big_sarray = free_big_sarray;
  1065.   emethods->free_big_barray = free_big_barray;
  1066.   emethods->free_all = free_all;
  1067.  
  1068.   /* Initialize list headers to empty */
  1069.   small_list = NULL;
  1070. #ifdef NEED_ALLOC_MEDIUM
  1071.   medium_list = NULL;
  1072. #endif
  1073.   small_sarray_list = NULL;
  1074.   small_barray_list = NULL;
  1075.   big_sarray_list = NULL;
  1076.   big_barray_list = NULL;
  1077.  
  1078.   jmem_init(emethods);        /* system-dependent initialization */
  1079.  
  1080.   /* Check for an environment variable JPEGMEM; if found, override the
  1081.    * default max_memory setting from jmem_init.  Note that a command line
  1082.    * -m argument may again override this value.
  1083.    * If your system doesn't support getenv(), define NO_GETENV to disable
  1084.    * this feature.
  1085.    */
  1086. #ifndef NO_GETENV
  1087.   { char * memenv;
  1088.  
  1089.     if ((memenv = getenv("JPEGMEM")) != NULL) {
  1090.       long lval;
  1091.       char ch = 'x';
  1092.  
  1093.       if (sscanf(memenv, "%ld%c", &lval, &ch) > 0) {
  1094.     if (ch == 'm' || ch == 'M')
  1095.       lval *= 1000L; 
  1096.     emethods->max_memory_to_use = lval * 1000L;
  1097.       }
  1098.     }
  1099.   }
  1100. #endif
  1101.  
  1102. }
  1103.